package com.cskaoyan.aspect.admin;

import com.cskaoyan.bean.BaseRespVo;
import com.cskaoyan.bean.admin.Admin;
import com.cskaoyan.bean.admin.Log;
import com.cskaoyan.mapper.admin.LogMapper;
import com.cskaoyan.utils.ValidationUtil;
import lombok.SneakyThrows;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 类<code>Doc</code>用于：TODO 切面类：参数验证与日志生成
 *
 * @author Acher
 * @version 1.0
 * @date 2021-10-21
 */
@Aspect
@Component
public class AdminValidationAndLogAspect {

    private static final Map<String, String> map = new HashMap<>();

    static {
        map.put("login", "登录");
        map.put("logout", "退出");
        map.put("create", "添加管理员");
        map.put("update", "编辑管理员");
        map.put("delete", "删除管理员");
        map.put("deleteOrder", "删除订单");
    }

    @Autowired
    LogMapper logMapper;

    /**
     * 方法<code>around</code>作用为：admin包下的参数验证与
     * 登录、退出、管理员增删改、删除订单的日志监控与生成
     *
     * @author Acher
     * @param joinPoint
     * @throws
     * @return java.lang.Object
     */
    @SneakyThrows
    @Around("execution(* com..controller.admin..*(..)) || " +
            "execution(* com..controller.mall..OrderController.deleteOrder(..))")
    public Object around(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();// 获取参数
        if (args != null) {
            for (Object arg : args) {
                if (arg instanceof BindingResult) {// 参数验证
                    ValidationUtil.valid(((BindingResult) arg));
                }
            }
        }
        BaseRespVo proceed = (BaseRespVo) joinPoint.proceed();
        String methodName = joinPoint.getSignature().getName();
        if (map.containsKey(methodName)) {// 生成日志
            extracted(proceed, methodName);
        }
        return proceed;
    }

    /**
     * 方法<code>afterReturningByLoginException</code>作用为：记录异常信息到日志
     *
     * @author Acher
     * @param result
     * @throws
     * @return void
     */
    @AfterReturning(value = "execution(* com..exception.admin..LoginExceptionControllerAdvice.*(..))", returning = "result")
    public void afterReturningByLoginException(Object result) {
        BaseRespVo respVo = (BaseRespVo) result;
        extracted(respVo, "login");
    }

    /**
     * 方法<code>extracted</code>作用为：生成日志并写入数据库
     *
     * @author Acher
     * @param result
	 * @param methodName
     * @throws
     * @return void
     */
    private void extracted(BaseRespVo result, String methodName) {
        Subject subject = SecurityUtils.getSubject();
        Log log = new Log();
        String host = subject.getSession().getHost();// ip
        if (subject.getPrincipals() == null) {// 根据用户信息是否存在判断是否为匿名用户
            log.setAdmin("匿名用户");
        } else {
            Admin principal = (Admin) subject.getPrincipals().getPrimaryPrincipal();
            log.setAdmin(principal.getUsername());
        }
        // username
        // login 👉 登录
        // date
        Date date = new Date(System.currentTimeMillis());
        log.setIp(host);
        String action = map.get(methodName);// 方法所对应的操作动作
        log.setType(1);// 操作类别 👉 系统模块=1
        if ("删除订单".equals(action))  log.setType(2);// 操作类别 👉 订单模块=2
        log.setAction(action);
        log.setAddTime(date);
        log.setUpdateTime(date);
        log.setDeleted(false);
        if (result.getErrno() == 0) {// 根据响应码判断操作是否成功
            log.setStatus(true);
        } else if (result.getErrno() == 500) {
            log.setStatus(false);
            log.setResult(result.getErrmsg());// 生成操作失败对应的信息
        }
        addLogToDb(log);
    }

    private void addLogToDb(Log log) {
        if (logMapper.insert(log) != 1)
            System.out.println("日志添加失败");
    }
}
